package com.weishi.common.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;

import java.util.*;
import java.util.Map.Entry;

public class WSRedisClient {
    private static JedisPool pool;
    private static WSRedisClient redisClient = null;
    public static long Lock_Max_Lifecycle = 24*60*60*1000L;

    /**
     * 通过静态工厂方法来沟通对象，复用对象，避免每次重新产生新对象
     */
    public static WSRedisClient newInstance(String host, int port, String password) {
        return newInstance(host, port, password, 0);
    }

    /**
     * 通过静态工厂方法来沟通对象，复用对象，避免每次重新产生新对象
     */
    public static WSRedisClient newInstance(String host, int port, String password, int dbIndex) {
        if (null != redisClient) {
            return redisClient;
        }

        synchronized (WSRedisClient.class) {
            if (null != redisClient) {
                return redisClient;
            }

            redisClient = new WSRedisClient(host, port, password, dbIndex);
            return redisClient;
        }
    }

    private WSRedisClient(String host, int port, String password) {
        this(host, port, password, 0);
    }

    private WSRedisClient(String host, int port, String password, int database) {
        if (null != pool) {
            return;
        }

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMinIdle(5);
        config.setMaxIdle(5);// 最大空闲连接数
        config.setMaxTotal(40);// 最大连接数
        config.setMaxWaitMillis(60000);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(false);

        pool = new JedisPool(config, host, port, 100000, password, database);
    }

    public Jedis getConnection() {
        Jedis jedis = pool.getResource();
        return jedis;
    }

    public void returnConnection(Jedis conn) {
        if(null != conn && null != pool) {
            conn.close();
        }
    }

    public Set<String> keys(String pattern) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.keys(pattern);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);			
        }
    }

    public Long incrBy(String key, long value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.incrBy(key, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);			
        }
    }

    public Double incrByFloat(String key, double value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.incrByFloat(key, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public void decrBy(String key, long value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.decrBy(key, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public boolean lock(String key, long timeout) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            long now = System.currentTimeMillis();
            do {
                long ret = conn.setnx(key, key);
                if(1 == ret) {
                    conn.pexpire(key, Lock_Max_Lifecycle);
                    return Boolean.TRUE;
                } else {
                    LoggerUtils.getLogger().info("failed to get redis lock: {}", key);
                }

                if(0 == timeout) {
                    break;
                }

                Thread.sleep(100L);
            } while((System.currentTimeMillis() - now) < timeout);

            return Boolean.FALSE;
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);            
        }

        return Boolean.FALSE;
    }

    public void unlock(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.del(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);            
        }
    }

    public void set(String key, String value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.set(key, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);			
        }
    }

    public void set(String key, String value, long expireMS) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            //            conn.set(key, value, "NX", "PX", expireMS);
            conn.set(key, value);
            conn.pexpire(key, expireMS);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);            
        }
    }

    public String get(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.get(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);			
        }
    }

    public String getSet(String key, String value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.getSet(key, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);			
        }
    }

    public void del(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.del(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public void del(String... keys) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.del(keys);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public boolean exists(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.exists(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return false;
        } finally {
            this.returnConnection(conn);
        }
    }

    public void expire(String key, long milliseconds) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            if (conn.exists(key)) {
                conn.pexpire(key, milliseconds);
            }
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    /**
     * 针对批量key做过期时间 单位秒
     * @param keyList
     * @param seconds
     */
    public void setBatchExpire(Collection<String> keyList, int seconds) {
        if(keyList.size() == 0) {
            return;
        }

        Jedis conn = null;
        try {
            conn = this.getConnection();
            Pipeline pipe = conn.pipelined();
            for(String key : keyList) {
                pipe.expire(key, seconds);
            }
            pipe.sync();
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public void hset(String key, String field, String value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.hset(key, field, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    /** 批量hash set 服务 */
    public void hmset(String key, Map<String, String> hash) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.hmset(key, hash);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public void hsetBatch(String key, Map<String,String> params) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            Pipeline p = conn.pipelined();

            Iterator<Entry<String, String>> it = params.entrySet().iterator();
            while(it.hasNext()){
                Entry<String, String> entry = it.next();
                String field = entry.getKey();
                String value = entry.getValue();
                p.hset(key, field, value);
            }
            p.sync();
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public void hsetBatchObj(String key, Map<String,Object> params) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            Pipeline p = conn.pipelined();

            Iterator<Entry<String, Object>> it = params.entrySet().iterator();
            while(it.hasNext()){
                Entry<String, Object> entry = it.next();
                String field = entry.getKey();
                String value = String.valueOf(entry.getValue());
                p.hset(key, field, value);
            }
            p.sync();
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public void hdel(String key, String... fields) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.hdel(key, fields);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public Set<String> hkeys(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.hkeys(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Map<String, String> hgetAll(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.hgetAll(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public String hget(String key, String field) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.hget(key, field);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Long hincrBy(String key, String field, Long value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.hincrBy(key, field, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Double hincrBy(String key, String field, double value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.hincrByFloat(key, field, value);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Long rpush(byte[] key, byte[] strings) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.rpush(key, strings);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Long lpush(byte[] key, byte[] strings) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.lpush(key, strings);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Long rpush(String key, String... strings) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.rpush(key, strings);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Long lpush(String key, String... strings) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.lpush(key, strings);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public byte[] lpop(byte[] key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.lpop(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public byte[] rpop(byte[] key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.rpop(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public String lpop(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.lpop(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public String rpop(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.rpop(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public List<String> lrange(String key, long start, long end) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.lrange(key, start, end);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public long llen(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.llen(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return 0;
        } finally {
            this.returnConnection(conn);
        }
    }

    public String ltrim(String key, long start, long end) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.ltrim(key, start, end);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public long sadd(String key, String ... members) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.sadd(key, members);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return 0;
        } finally {
            this.returnConnection(conn);
        }
    }

    public long scard(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.scard(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return 0;
        } finally {
            this.returnConnection(conn);
        }
    }

    public String spop(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.spop(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public long srem(String key, String ... members) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.srem(key, members);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return 0;
        } finally {
            this.returnConnection(conn);
        }
    }

    public Set<String> smembers(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.smembers(key);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return null;
        } finally {
            this.returnConnection(conn);
        }
    }

    public boolean sismember(String key, String member) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            return conn.sismember(key, member);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
            return false;
        } finally {
            this.returnConnection(conn);
        }
    }

    public void publish(String channel, String message) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            conn.publish(channel, message);
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }

    public boolean setBit(String key, long offerset, String value) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            boolean result = conn.setbit(key, offerset, value);
            return result;
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
        return false;
    }

    public boolean getBit(String key, long offerset) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            boolean result = conn.getbit(key, offerset);
            return result;
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
        return false;
    }

    public long bitCount(String key) {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            long result = conn.bitcount(key);
            return result;
        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
        return 0;
    }

    public void mgettest() {
        Jedis conn = null;
        try {
            conn = this.getConnection();
            byte[] key1 = new byte[0];
            conn.mget("");

        } catch (Exception e) {
            LoggerUtils.getLogger().error("Redis服务不可用", e);
        } finally {
            this.returnConnection(conn);
        }
    }
}
